package bs.util.tool.ant;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.util.Date;
import java.util.Properties;

import javax.swing.JTextArea;

import bs.util.web.tool.eclipse.ProjectPropertiesDeal;
import bs.util.web.tool.eclipse.ProjectPropertiesDealInterface;

/**
 * Ant Script Auto Build.
 * 
 * @author baishui2004
 * @version 1.1
 * @date 2013-4-5
 */
public class AntScriptAutoBuild {

	/**
	 * Eclipse的Java Project、Dynamic Web Project或者MyEclipse的Web Project绝对路径地址.
	 */
	private String projectPath;

	/**
	 * Project properties.
	 */
	private ProjectPropertiesDealInterface projectProperties;

	/**
	 * AntScriptAutoBuild conf properties.
	 */
	private Properties confProperties;

	/**
	 * 生成Ant 脚本属性文件路径.
	 */
	private String antPropertiesFilePath;

	/**
	 * 生成Ant 脚本文件路径.
	 */
	private String antScriptFilePath;

	/**
	 * 是否备份已存在的同名构建属性脚本及脚本.
	 */
	private boolean isBak = true;

	public void setIsBak(boolean isBak) {
		this.isBak = isBak;
	}

	/**
	 * 运行日志输出文本域.
	 */
	private JTextArea runLogTextArea;

	public void setRunLogTextArea(JTextArea runLogTextArea) {
		this.runLogTextArea = runLogTextArea;
	}

	/**
	 * 输出.
	 */
	private void print(String log) {
		if (runLogTextArea != null) {
			runLogTextArea.append(log + "\n");
		} else {
			System.out.print(log + "\n");
		}
	}

	/**
	 * Main入口.
	 * 
	 * <pre>
	 * 只接受传入一个参数, 即Eclipse的Dynamic Web Project或者MyEclipse的Web Project绝对路径地址.
	 * </pre>
	 */
	public static void main(String[] args) throws IOException, URISyntaxException {
		if (args.length == 2) {
			AntScriptAutoBuild build = new AntScriptAutoBuild();
			ProjectPropertiesDealInterface propertiesDeal = new ProjectPropertiesDeal();
			propertiesDeal.deal(args[0]);
			build.autoBuild(args[0], propertiesDeal, args[1]);
		} else {
			throw new IllegalArgumentException("Parameters error.");
		}
	}

	/**
	 * 根据Eclipse/MyEclipse Project属性创建Ant脚本.
	 */
	public void autoBuild(String projectPath, ProjectPropertiesDealInterface properties, String taskNames)
			throws IOException, URISyntaxException {
		this.projectPath = projectPath;
		this.projectProperties = properties;
		this.antPropertiesFilePath = this.projectPath + "/build.properties";
		this.antScriptFilePath = this.projectPath + "/build.xml";

		this.confProperties = new Properties();
		InputStream in = null;
		try {
			in = new FileInputStream(new File(getActualPath("conf/AntScriptAutoBuild/conf.properties")));
			this.confProperties.load(in);
		} finally {
			if (in != null) {
				in.close();
			}
		}

		String actualTask = getActualAndSortTaskNames(taskNames);
		String specialTask = getSpecialAndSortTaskNames(taskNames);
		String printTask = actualTask + "," + specialTask;
		if (printTask.startsWith(",")) {
			printTask = printTask.substring(1);
		}
		if (printTask.endsWith(",")) {
			printTask = printTask.substring(0, printTask.length() - 1);
		}
		print("Ant Task: " + printTask.replace(",", ", ") + ".\n");

		appendPropertiesAndScriptFile(actualTask, specialTask);
		autoReBuildPropertiesFile();

		print("\nAuto build Ant Script Done.");
	}

	/**
	 * 重新排序Ant Task.
	 */
	private String getActualAndSortTaskNames(String taskNames) {
		String baseTask = this.confProperties.getProperty("baseTask").trim();
		String javaTask = this.confProperties.getProperty("javaTask").trim();
		String javaWebTask = this.confProperties.getProperty("javaWebTask").trim();
		String generalTask = this.confProperties.getProperty("generalTask").trim();

		String allTask = baseTask + "," + javaTask + "," + javaWebTask + "," + generalTask + ",";
		String[] tasks = allTask.split(",");
		StringBuilder sortTaskName = new StringBuilder();
		for (String task : tasks) {
			if (("," + taskNames.toLowerCase() + ",").contains("," + task.toLowerCase() + ",")) {
				if (!(!this.projectProperties.isJavaWebProject() && ("," + javaWebTask.toLowerCase() + ",")
						.contains("," + task.toLowerCase() + ","))) {
					sortTaskName.append("," + task);
				}
			}
		}

		return sortTaskName.length() != 0 ? sortTaskName.substring(1) : sortTaskName.toString();
	}

	/**
	 * 特殊Ant Task.
	 */
	private String getSpecialAndSortTaskNames(String taskNames) {
		String specialTask = this.confProperties.getProperty("specialTask").trim();
		String[] tasks = specialTask.split(",");
		StringBuilder sortTaskName = new StringBuilder();
		for (String task : tasks) {
			if (("," + taskNames.toLowerCase() + ",").contains("," + task.toLowerCase() + ",")) {
				sortTaskName.append("," + task);
			}
		}
		return sortTaskName.length() != 0 ? sortTaskName.substring(1) : sortTaskName.toString();
	}

	/**
	 * 组装build.properties及build.xml.
	 */
	private void appendPropertiesAndScriptFile(String taskNames, String specialTaskNames) throws IOException,
			URISyntaxException {
		// build.properties
		appendFile(this.antPropertiesFilePath, getActualPath("conf/AntScriptAutoBuild/properties/"), taskNames,
				"#Auto build properties, " + new Date() + ".\n", "");

		// build.xml
		String xmlHeader = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" + "\n" + "<project name=\""
				+ this.projectProperties.getProjectName() + "\" default=\"all\" basedir=\".\">" + "\n\n"
				+ "	<!-- properties属性配置文件 -->\n" + "	<property file=\"build.properties\" />";
		String xmlEnd = "\n	<!-- 全部任务 -->\n" + "	<target name=\"all\" depends=\"";
		appendFile(this.antScriptFilePath, getActualPath("conf/AntScriptAutoBuild/script/"), taskNames, xmlHeader,
				xmlEnd + taskNames + "\" />" + "\n\n" + "</project>");

		// 特殊任务，此类任务每个任务单独生成一个构建脚本build-taskName.xml
		if (specialTaskNames.length() != 0) {
			String[] specialTasks = specialTaskNames.split(",");
			for (String task : specialTasks) {
				appendFile(this.antScriptFilePath.substring(0, this.antScriptFilePath.length() - 4) + "-" + task
						+ ".xml", getActualPath("conf/AntScriptAutoBuild/script/"), task, xmlHeader, xmlEnd + task
						+ "\" />" + "\n\n" + "</project>");
			}
		}
	}

	/**
	 * 根据taskNames拼装文件.
	 */
	private void appendFile(String mergerFilePath, String folderPath, String taskNames, String header, String end)
			throws IOException {
		File mergerFile = new File(mergerFilePath);

		if (mergerFile.exists()) {
			if (isBak) {
				String reNameMergerFilePath = mergerFilePath + "." + new Date().getTime() + "bak";
				File reNameMergerFile = new File(reNameMergerFilePath);
				mergerFile.renameTo(reNameMergerFile);
				print("old file \"" + mergerFile.getName() + "\" renamed to " + "\"" + reNameMergerFile.getName()
						+ "\".");
			} else {
				print("File \"" + mergerFile.getName() + "\" exist but be covered.");
			}
		}

		StringBuilder inSb = new StringBuilder(header + "\n");
		String[] tasks = taskNames.split(",");
		for (String task : tasks) {
			inSb.append("\n");
			appendFile(inSb, folderPath + task);
		}
		inSb.append("\n" + end);

		OutputStream fos = new FileOutputStream(mergerFile);
		// 不采用fos.write(bytes)是为了保证输出文件的存储格式为"UTF-8"
		// fos.write(inSb.toString().getBytes());
		// 使用下面的方式能够保证输出文件的存储格式为"UTF-8"
		Writer writer = new OutputStreamWriter(fos, "UTF-8");
		writer.write(inSb.toString());
		writer.flush();
		writer.close();
		fos.close();

		if (!System.getProperty("os.name").contains("Windows")) {
			try {
				Runtime.getRuntime().exec(new String[] { "chmod", "755", mergerFilePath }).waitFor();
			} catch (Throwable e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 拼装文件.
	 */
	private void appendFile(StringBuilder inSb, String filePath) throws IOException {
		File file = new File(filePath);
		if (!file.exists()) {
			return;
		}
		InputStream in = new FileInputStream(file);
		InputStreamReader reader = new InputStreamReader(in, "UTF-8");
		BufferedReader bfReader = new BufferedReader(reader);
		while (true) {
			String line = bfReader.readLine();
			if (line == null) {
				break;
			}
			inSb.append(line).append("\n");
		}
		in.close();
		reader.close();
		bfReader.close();
	}

	/**
	 * 根据Eclipse/MyEclipse Project属性重设build.properties部分属性值.
	 */
	private void autoReBuildPropertiesFile() throws IOException {
		File propertiesFile = new File(this.antPropertiesFilePath);
		InputStream in = new FileInputStream(propertiesFile);
		InputStreamReader reader = new InputStreamReader(in, "UTF-8");
		BufferedReader bfReader = new BufferedReader(reader);
		StringBuilder inSb = new StringBuilder();
		while (true) {
			String line = bfReader.readLine();
			if (line == null) {
				break;
			}
			if (line.startsWith("project.name=")) {
				line = "project.name=" + this.projectProperties.getProjectName();
			} else if (line.startsWith("dir.lib=") && this.projectProperties.isJavaWebProject()) {
				line = "dir.lib=" + this.projectProperties.getWebappPath() + "/WEB-INF/lib";
			} else if (line.startsWith("dir.web=") && this.projectProperties.isJavaWebProject()) {
				line = "dir.web=" + this.projectProperties.getWebappPath();
			} else if (line.startsWith("project.compile.source=")) {
				line = "project.compile.source=" + this.projectProperties.getCompileSource();
			} else if (line.startsWith("project.compile.target=")) {
				line = "project.compile.target=" + this.projectProperties.getCompileTarget();
			} else if (line.startsWith("dir.src=")) {
				line = "dir.src=" + this.projectProperties.getJavaSourcesPath()[0];
			} else if (line.startsWith("dir.classes=")) {
				line = "dir.classes=" + this.projectProperties.getOutputPath();
			}
			inSb.append(line).append("\n");
		}
		OutputStream fos = new FileOutputStream(propertiesFile);
		// 不采用fos.write(bytes)是为了保证输出文件的存储格式为"UTF-8"
		// fos.write(inSb.toString().getBytes());
		// 使用下面的方式能够保证输出文件的存储格式为"UTF-8"
		Writer writer = new OutputStreamWriter(fos, "UTF-8");
		writer.write(inSb.toString());
		writer.flush();
		writer.close();

		in.close();
		reader.close();
		bfReader.close();
		fos.close();
	}

	/**
	 * 获取与Jar lib目录同级的文件(夹)或同级文件夹的子文件(夹)的绝对路径.
	 */
	public static String getActualPath(String path) {
		return LIB_PARENT_PATH + path;
	}

	/**
	 * Jar lib目录的父路径.
	 */
	private static String LIB_PARENT_PATH = getLibParentPath();

	/**
	 * 获取Jar lib目录的父路径.
	 */
	private static String getLibParentPath() {
		String classResource = AntScriptAutoBuild.class.getName().replace(".", "/") + ".class";
		String path = "";
		try {
			path = AntScriptAutoBuild.class.getClassLoader().getResource(classResource).getPath();
			path = path.replace("+", "%2b"); // "+"号decode后为空格" "，"%2b"号decode后为"+"号
			path = URLDecoder.decode(path, "UTF-8");
			path = path.substring(0, path.length() - classResource.length());
			if (path.contains(".jar!")) {
				path = path.substring(5, path.indexOf(".jar!") + 4);
				path = path.substring(0, path.lastIndexOf("/"));
				path = path.substring(0, path.length() - "lib".length());
			} else {
				path = path.substring(0, path.length() - "classes".length() - 1);
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return path;
	}

}
